#include <SDL/SDL.h>
#include <fstream>
#include <stdexcept>
#include <sstream>
#include "heroz.hh"
#include "heroz_basic.hh"
#include "guru_meditation.h"

Basic::FloatType HeroBasic::exec_peek(FloatType addr) {
  if(addr >= 0 && addr < 0x10000) {
    return heroz->memory[static_cast<unsigned short>(addr)];
  }
  throw IllegalQuantityError();
}

void HeroBasic::exec_poke2(FloatType addr, FloatType val) {
  if(addr >= 0 && addr < 0x10000) {
    heroz->memory[static_cast<unsigned short>(addr) & 0xFFFF] = val;
    return;
  }
  throw IllegalQuantityError();
}

void HeroBasic::unknown_command(int cmd) {
  assert(cmd >= 1000);
  switch(cmd) {
  case 1000:
    {
      Uint8 r(numexpr());
      if(beg == end || !dynamic_cast<const TokSep *>(*beg++)) throw SyntaxError();
      Uint8 g(numexpr());
      if(beg == end || !dynamic_cast<const TokSep *>(*beg++)) throw SyntaxError();
      Uint8 b(numexpr());
      SDL_FillRect(heroz->display, NULL, SDL_MapRGB(heroz->display->format, r, g, b));
    }
    break;
  case 1001:
    {
      short x(numexpr());
      if(beg == end || !dynamic_cast<const TokSep *>(*beg++)) throw SyntaxError();
      short y(numexpr());
      if(beg == end || !dynamic_cast<const TokSep *>(*beg++)) throw SyntaxError();
      string txt(strexpr());
      heroz->font->print_at(x, y, txt);
    }
    break;
  case 1002:
    {
      short x(numexpr());
      if(x >= 0) {
	if(x >= heroz->muzaks.size()) throw IllegalQuantityError();
	Mix_FadeInMusic(heroz->muzaks.at(x), -1, 20);
      } else { //x < 0
	Mix_FadeOutMusic(-x);
      }
    }
    break;
  default:
    throw SyntaxError();
  }
}


void HeroBasic::print_error(unsigned short line, const Error &error) {
  ostringstream out, out2;
  int mb;

  out << '?' << error.what();
  heroz->font->print_at(0, heroz->display->h - 40, out.str());
  out2 << "in line " << line;
  heroz->font->print_at(0, heroz->display->h - 20, out2.str());
  SDL_Flip(heroz->display);
  mb = guru_meditation(GM_FLAGS_RECOVERY | GM_FLAGS_CHOICE | GM_FLAGS_ABORTIFY, GM_SS_Basic | GM_GE_CodeFlow | line, NULL);
  if(mb != 1) throw error;
}


HeroBasic *load_program(const char *path, HeroZ *heroz) {
  HeroBasic *basic;
  std::string tmp;
  ifstream in(string(path) + "/code.bas");
  KeywordMapType keywords(default_keywords);

  keywords["CLEARRGB"] = 1000;
  keywords["GPRINT"] = 1001;
  keywords["MELODY"] = 1002;
  if(!in) throw std::runtime_error("can not open basic file");
  basic = new HeroBasic(heroz);
  while(getline(in, tmp)) {
    if(tmp.empty() || in.eof()) break;
    istringstream line(tmp);
    unsigned short lineno;

    if(line >> lineno) {
      getline(line, tmp);
      TokenListType tokens(tokenise(keywords, tmp));
      basic->add_line(lineno, tokens);
    } else {
      std::ostringstream out;
      out << "Error after reading line " << lineno << ".\n";
      cerr << out.str();
      throw std::runtime_error(out.str());
    }
  }
  basic->var_int("RUNGAME") = 1;
  return basic;
}

void quit_program(HeroBasic *basic) {
  delete basic;
}

void init_basic(HeroZ *heroz) {
  heroz->basic->run();
}
